// Copyright (C) 2012 Denis Shienkov <denis.shienkov@gmail.com>
// Copyright (C) 2012 Laszlo Papp <lpapp@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "settingsdialog.h"
#include <QLabel>
#include <QMessageBox>
#include <QTimer>
#include <chrono>
#include <QTextCursor>
#include <QTextBlock>
#include <QTextDocument>
static constexpr std::chrono::seconds kWriteTimeout = std::chrono::seconds{5};
static int g_ins = 0,g_input = 0, g_output = 0;
//! [0]
MainWindow::MainWindow(const TASKBUS::cmdlineParser * cmdLine,QWidget *parent) :
	QMainWindow(parent),
	m_ui(new Ui::MainWindow),
	//! [0]
	m_status(new QLabel),
	m_settings(new SettingsDialog(this)),
	m_timer(new QTimer(this)),
	//! [1]
	m_serial(new QSerialPort(this)),
	cmdline(cmdLine),
	m_pRevThd(new reciv_thread(this))
{
	//! [1]
	m_ui->setupUi(this);
	m_ui->actionConnect->setEnabled(true);
	m_ui->actionDisconnect->setEnabled(false);
	m_ui->actionQuit->setEnabled(true);
	m_ui->actionConfigure->setEnabled(true);

	m_ui->statusBar->addWidget(m_status);

	initActionsConnections();

	connect(m_serial, &QSerialPort::errorOccurred, this, &MainWindow::handleError);
	connect(m_timer, &QTimer::timeout, this, &MainWindow::handleWriteTimeout);
	m_timer->setSingleShot(true);

	connect(m_serial, &QSerialPort::readyRead, this, &MainWindow::readData);
	connect(m_serial, &QSerialPort::bytesWritten, this, &MainWindow::handleBytesWritten);
	connect(m_pRevThd,&reciv_thread::new_package,this,&MainWindow::slot_new_taskpack,Qt::QueuedConnection);
	connect(m_pRevThd,&reciv_thread::sig_quit,this,&MainWindow::close);

	if (cmdLine)
	{
		SettingsDialog::Settings & p = m_settings->settings();
		p.name = QString::fromStdString(cmdLine->toString("name","COM1"));
		p.stringBaudRate = QString::fromStdString(cmdLine->toString("baudrate","9600"));
		p.stringDataBits = QString::fromStdString(cmdLine->toString("databits","8"));
		p.stringParity = QString::fromStdString(cmdLine->toString("parity","None"));
		p.stringStopBits = QString::fromStdString(cmdLine->toString("stopbits","1"));
		p.stringFlowControl = QString::fromStdString(cmdLine->toString("flowcontrol","None"));
		p.localEchoEnabled = cmdLine->toInt("localecho",1)==0?false:true;
		g_input = cmdLine->toInt("input",0);
		g_output = cmdLine->toInt("output",0);
		g_ins = cmdLine->toInt("instance",0);
		m_settings->fillStringsToUI();
		bool utf8 = cmdLine->toInt("utf8",1)==0?false:true;
		bool hex = cmdLine->toInt("hex",1)==0?false:true;
		m_ui->checkBox_utf8->setChecked(utf8);
		m_ui->checkBox_hex->setChecked(hex);
		if (g_ins)
		{
			openSerialPort();
			m_pRevThd->start();
		}
	}

}
//! [3]

MainWindow::~MainWindow()
{
	delete m_settings;
	m_pRevThd->terminate();

	delete m_ui;
}

//! [4]
void MainWindow::openSerialPort()
{
	const SettingsDialog::Settings & p = m_settings->settings();
	m_serial->setPortName(p.name);
	m_serial->setBaudRate(p.baudRate);
	m_serial->setDataBits(p.dataBits);
	m_serial->setParity(p.parity);
	m_serial->setStopBits(p.stopBits);
	m_serial->setFlowControl(p.flowControl);
	if (m_serial->open(QIODevice::ReadWrite)) {
		m_ui->console->setEnabled(true);
		m_ui->actionConnect->setEnabled(false);
		m_ui->actionDisconnect->setEnabled(true);
		m_ui->actionConfigure->setEnabled(false);
		showStatusMessage(tr("Connected to %1 : %2, %3, %4, %5, %6")
						  .arg(p.name, p.stringBaudRate, p.stringDataBits,
							   p.stringParity, p.stringStopBits, p.stringFlowControl));
	} else {
		m_ui->console->appendPlainText( m_serial->errorString());

		showStatusMessage(tr("Open error"));
	}
}
//! [4]

//! [5]
void MainWindow::closeSerialPort()
{
	if (m_serial->isOpen())
		m_serial->close();
	m_ui->console->setEnabled(false);
	m_ui->actionConnect->setEnabled(true);
	m_ui->actionDisconnect->setEnabled(false);
	m_ui->actionConfigure->setEnabled(true);
	showStatusMessage(tr("Disconnected"));
}
//! [5]

void MainWindow::about()
{
	QMessageBox::about(this, tr("About Serial Terminal"),
					   tr("The <b>Serial Terminal</b> example demonstrates how to "
						  "use the Qt Serial Port module in modern GUI applications "
						  "using Qt, with a menu bar, toolbars, and a status bar."));
}

//! [6]
void MainWindow::writeData(const QByteArray &data)
{
	const qint64 written = m_serial->write(data);
	m_serial->flush();
	if (written == data.size()) {
		m_bytesToWrite += written;
		m_timer->start(kWriteTimeout);
	} else {
		const QString error = tr("Failed to write all data to port %1.\n"
								 "Error: %2").arg(m_serial->portName(),
												  m_serial->errorString());
		showWriteError(error);
	}
}
//! [6]

//! [7]
void MainWindow::readData()
{
	const QByteArray data = m_serial->readAll();
	bool needInsert = false;
	if (g_output && g_ins)
	{
		TASKBUS::push_subject(g_output,g_ins,
							  data.length(),(const unsigned char *) data.constData());

		if (m_settings->settings().localEchoEnabled)
			needInsert = true;

	}
	else
		needInsert = true;

	if (!needInsert)
		return;
	if (m_ui->checkBox_hex->isChecked())
	{
		m_ui->console->appendPlainText(data.toHex()+"\n");
	}
	else
		m_ui->console->appendPlainText(ba2str(data));

}
//! [7]

//! [8]
void MainWindow::handleError(QSerialPort::SerialPortError error)
{
	if (error == QSerialPort::ResourceError) {
		m_ui->console->appendPlainText( m_serial->errorString());
		static QTextStream stm(stderr);
		stm << m_serial->errorString()<<"\n";
		stm.flush();
		//closeSerialPort();
	}
}
//! [8]

//! [9]
void MainWindow::handleBytesWritten(qint64 bytes)
{
	m_bytesToWrite -= bytes;
	if (m_bytesToWrite == 0)
		m_timer->stop();
}
//! [9]

void MainWindow::handleWriteTimeout()
{
	const QString error = tr("Write operation timed out for port %1.\n"
							 "Error: %2").arg(m_serial->portName(),
											  m_serial->errorString());
	showWriteError(error);
}

void MainWindow::initActionsConnections()
{
	connect(m_ui->actionConnect, &QAction::triggered, this, &MainWindow::openSerialPort);
	connect(m_ui->actionDisconnect, &QAction::triggered, this, &MainWindow::closeSerialPort);
	connect(m_ui->actionQuit, &QAction::triggered, this, &MainWindow::close);
	connect(m_ui->actionConfigure, &QAction::triggered, m_settings, &SettingsDialog::show);
	connect(m_ui->actionClear, &QAction::triggered, m_ui->console, &QPlainTextEdit::clear);
	connect(m_ui->actionAbout, &QAction::triggered, this, &MainWindow::about);
	connect(m_ui->actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt);
}

void MainWindow::showStatusMessage(const QString &message)
{
	m_status->setText(message);
	static QTextStream stm(stderr);
	stm << message<<"\n";
	stm.flush();
}

void MainWindow::showWriteError(const QString &message)
{
	m_ui->console->appendPlainText( message);
	static QTextStream stm(stderr);
	stm << message<<"\n";
	stm.flush();
}
QString MainWindow::ba2str(QByteArray arr)
{
	if (m_ui->checkBox_utf8->isChecked())
		return QString::fromUtf8(arr);
	else
		return QString::fromLocal8Bit(arr);
}
QByteArray MainWindow::str2ba(QString str)
{
	if (m_ui->checkBox_utf8->isChecked())
		return str.toUtf8();
	else
		return str.toLocal8Bit();
}
void MainWindow::on_pushButton_send_clicked()
{
	const SettingsDialog::Settings p = m_settings->settings();
	auto r = m_ui->cmd->textCursor().block().text();
	if (m_ui->checkBox_hex->isChecked())
	{
		QString hexStr = r.toUpper().trimmed();
		QByteArray arr = QByteArray::fromHex(hexStr.toUtf8());
		if (arr.size())
		{
			writeData(arr);
			if (p.localEchoEnabled)
				m_ui->console->appendPlainText(ba2str(arr));
		}

	}
	else
	{
		if (m_ui->checkBox_nr->isChecked())
			r += "\n";
		if (r.size())
		{
			writeData(str2ba(r));
			if (p.localEchoEnabled)
				m_ui->console->appendPlainText(r);
		}
	}
}


void MainWindow::on_pushButton_send_all_clicked()
{
	const SettingsDialog::Settings p = m_settings->settings();
	QTextDocument *layout = m_ui->cmd->document();
	int nblocks = layout->blockCount();
	for (int blk = 0; blk < nblocks;++blk)
	{
		auto r = layout->findBlockByNumber(blk).text();
		if (m_ui->checkBox_hex->isChecked())
		{
			QString hexStr = r.toUpper().trimmed();
			QByteArray arr = QByteArray::fromHex(hexStr.toUtf8());
			if (arr.size())
			{
				writeData(arr);
				if (p.localEchoEnabled)
					m_ui->console->appendPlainText(ba2str(arr));
			}

		}
		else
		{
			if (m_ui->checkBox_nr->isChecked())
				r += "\n";
			if (r.size())
			{
				writeData(str2ba(r));
				if (p.localEchoEnabled)
					m_ui->console->appendPlainText(r);
			}
		}
	}

}

void MainWindow::slot_new_taskpack(QByteArray package)
{
	using namespace TASKBUS;
	const subject_package_header * pheader = (const subject_package_header *)
			package.constData();
	const int pts = pheader->data_length;
	const unsigned char * fdata =  (const unsigned char *)(package.constData()+sizeof(subject_package_header));
	if (pts<1 || pheader->subject_id!=g_input)
		return;

	writeData(QByteArray((char *)fdata,pts));
}
